package com.library.exception;

import com.library.dto.ApiResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import java.util.HashMap;
import java.util.Map;

/**
 * 全局异常处理器
 * 统一处理应用中抛出的各种异常，返回统一的错误响应格式
 * 使用@RestControllerAdvice注解，可以处理所有Controller抛出的异常
 */
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
    
    /**
     * 处理业务异常
     * 当业务逻辑中抛出BusinessException时，会进入此方法处理
     * 
     * @param e 业务异常对象
     * @return 错误响应，包含错误消息和错误码
     */
    @ExceptionHandler(BusinessException.class)
    public ResponseEntity<ApiResponse<Object>> handleBusinessException(BusinessException e) {
        log.warn("业务异常: {}", e.getMessage());
        ApiResponse<Object> response = ApiResponse.error(e.getMessage(), e.getCode());
        return ResponseEntity.status(e.getHttpStatus()).body(response);
    }

    /**
     * 处理资源未找到异常
     * 当请求的资源不存在时，返回404状态码
     * 
     * @param e NotFoundException异常对象
     * @return 404错误响应
     */
    @ExceptionHandler(NotFoundException.class)
    public ResponseEntity<ApiResponse<Object>> handleNotFoundException(NotFoundException e) {
        log.warn("资源未找到: {}", e.getMessage());
        ApiResponse<Object> response = ApiResponse.error(e.getMessage(), "NOT_FOUND");
        return ResponseEntity.status(HttpStatus.NOT_FOUND).body(response);
    }

    /**
     * 处理未授权异常
     * 当用户未登录或token无效时，返回401状态码
     * 
     * @param e UnauthorizedException异常对象
     * @return 401错误响应
     */
    @ExceptionHandler(UnauthorizedException.class)
    public ResponseEntity<ApiResponse<Object>> handleUnauthorizedException(UnauthorizedException e) {
        log.warn("未授权: {}", e.getMessage());
        ApiResponse<Object> response = ApiResponse.error(e.getMessage(), "UNAUTHORIZED");
        return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(response);
    }

    /**
     * 处理禁止访问异常
     * 当用户权限不足时，返回403状态码
     * 
     * @param e ForbiddenException异常对象
     * @return 403错误响应
     */
    @ExceptionHandler(ForbiddenException.class)
    public ResponseEntity<ApiResponse<Object>> handleForbiddenException(ForbiddenException e) {
        log.warn("禁止访问: {}", e.getMessage());
        ApiResponse<Object> response = ApiResponse.error(e.getMessage(), "FORBIDDEN");
        return ResponseEntity.status(HttpStatus.FORBIDDEN).body(response);
    }

    /**
     * 处理参数验证异常
     * 当使用@Valid注解验证请求参数失败时，会进入此方法
     * 返回详细的字段验证错误信息
     * 
     * @param e MethodArgumentNotValidException异常对象
     * @return 400错误响应，包含所有验证失败的字段和错误消息
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<ApiResponse<Map<String, String>>> handleValidationException(MethodArgumentNotValidException e) {
        // 收集所有验证错误
        Map<String, String> errors = new HashMap<>();
        e.getBindingResult().getAllErrors().forEach(error -> {
            String fieldName = ((FieldError) error).getField();
            String errorMessage = error.getDefaultMessage();
            errors.put(fieldName, errorMessage);
        });
        log.warn("参数验证失败: {}", errors);
        ApiResponse<Map<String, String>> response = ApiResponse.error("参数验证失败", "VALIDATION_ERROR");
        response.setData(errors);
        return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(response);
    }

    /**
     * 处理所有其他异常
     * 作为兜底处理，捕获所有未被其他处理器处理的异常
     * 避免向用户暴露系统内部错误信息
     * 
     * @param e Exception异常对象
     * @return 500错误响应，返回通用错误消息
     */
    @ExceptionHandler(Exception.class)
    public ResponseEntity<ApiResponse<Object>> handleException(Exception e) {
        // 记录完整的异常堆栈信息，便于排查问题
        log.error("系统异常", e);
        // 返回通用错误消息，不暴露系统内部错误
        ApiResponse<Object> response = ApiResponse.error("系统内部错误，请稍后重试");
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response);
    }
}
